package scenetracer.core.primitive;

import scenetracer.core.Primitive;
import scenetracer.core.Ray;
import scenetracer.core.primitive.IntersectionState.IntersectionKind;
import scenetracer.math.Matrix4;
import scenetracer.math.Point2;
import scenetracer.math.Point3;
import scenetracer.math.Vector3;

public class Sphere extends Primitive {

	private Matrix4 transf;
	private Point3 c;
	private float sqrR;
	
	
	public Sphere() {
		c = new Point3(0, 0, 0);
		sqrR = 1f;		
	}
	
	public Sphere(Point3 center, float radius){
		this.setC(center);
		this.setR(radius);
	}
	
	public void setR(float radius) {
		this.sqrR = (float) Math.pow(radius, 2);
	}

	public float getR() {
		return (float)Math.sqrt(sqrR);
	}
	
	public void setC(Point3 center) {
		this.c = center;
	}

	public Point3 getC() {
		return c;
	}

	public Matrix4 getTransform() {
		return transf;
	}

	public void setTransform(Matrix4 transform) {
		this.transf = transform;		
		Point3 pointToScale = Point3.add(c, new Vector3(sqrR, 0 , 0), new Point3());		
		c = this.transf.transformP(c);		
		pointToScale = this.transf.transformP(pointToScale);
		sqrR = pointToScale.distanceTo(c);		
	}

	@Override
	public Point2 getTextureCoords(Point3 point) {
		Vector3 rp = Point3.sub(point, c);
		
        double v = rp.z / sqrR;
        
        if (Math.abs(v) > 1) v -= 1 * Math.signum(v);
        v = Math.acos(v);
        
        double u = rp.x / (sqrR * Math.sin(v));
        
        if (Math.abs(u) > 1) u = Math.signum(u);
        u = Math.acos(u);               
        
        if (rp.y < 0)
            u = -u;
        if (rp.z < 0)
            v = v + Math.PI;
        
        if (Double.isNaN(u)) {
        	int a = 0; a++;
        }
        
        u = (u / (2 * Math.PI));
        v = (v / Math.PI);
        
        if (u > 1) u -= 1;
        if (u < 0) u += 1;
        
        if (v > 1) v -= 1;
        if (v < 0) v += 1;
        
        return new Point2(u , v);						
	}
	
	@Override
	public Vector3 getNormal(IntersectionState i) {
		Point3 point = i.getIntersectionPoint();
		return Point3.sub(point, c, new Vector3()).normalize();
	}
	
	
    @Override
	public boolean Intersect(Ray ray, IntersectionState intersection) {
		
		Vector3 oc = Point3.sub(ray.getOrigin(), c, new Vector3());
		float b = -Vector3.dot( oc, ray.getDirection());
		float det = (b * b) - Vector3.dot(oc, oc) + sqrR;
		
		if (det > 0)
		{
			det = (float) Math.sqrt(det);
			float i1 = b - det;
			float i2 = b + det;
			if (i2 > 0)
			{
				Point3 intersectionPoint;
				if (i1 < 0) 
				{		
					intersectionPoint = Point3.add(ray.getOrigin(), new Vector3(ray.getDirection()).mul(i2),new Point3());	
					intersection.setIntersectionKind(IntersectionKind.IN);			
				}
				else
				{
					intersectionPoint = Point3.add(ray.getOrigin(), new Vector3(ray.getDirection()).mul(i1), new Point3());	
					intersection.setIntersectionKind(IntersectionKind.OUT);

				}
				
				intersection.setIntersectionPoint(intersectionPoint);
				intersection.setNormal(getNormal(intersection));
				intersection.setPrimitive(this);
				
				return true;
			}
		}
		
		return false;
	}

    
}
